package obj

import "maps"

type BuiltinFunction func(args []Object, env *Environment) Object

type Builtin struct {
	Fn BuiltinFunction
}

func (b *Builtin) Type() ObjectType {
	return BUILTIN_OBJ
}

func (b *Builtin) String() string {
	return "builtin function"
}

func (b *Builtin) Inspect() string {
	return "builtin function"
}

type Environment struct {
	store map[string]Object
	outer *Environment
}

func NewEnvironment() *Environment {
	return &Environment{store: make(map[string]Object), outer: nil}
}

func NewEnclosedEnvironment(outer *Environment) *Environment {
	env := NewEnvironment()
	env.outer = outer
	return env
}

func (e *Environment) Get(name string) (Object, bool) {
	obj, ok := e.store[name]
	if !ok && e.outer != nil {
		obj, ok = e.outer.Get(name)
	}
	return obj, ok
}

func (e *Environment) Set(name string, val Object) Object {
	e.store[name] = val
	return val
}

// Update updates an existing variable in the environment.
// Returns true if the variable was found and updated, false otherwise.
func (e *Environment) Update(name string, val Object) bool {
	// Check current environment
	_, exists := e.store[name]
	if exists {
		e.store[name] = val
		return true
	}
	// Check outer environments
	if e.outer != nil {
		return e.outer.Update(name, val)
	}
	// Variable not found in any environment
	return false
}

func (e *Environment) Copy() *Environment {
	copiedStore := make(map[string]Object)
	maps.Copy(copiedStore, e.store)

	return &Environment{
		store: copiedStore,
		outer: e.outer,
	}
}
